/********************************************************************
* Name: X86AsmGenerator.cpp: 
*
* Description:
*   Generate Pseudo-x86-asm-code defined within 
*   X86Code abstract structure from IL representation. 
*   
*
* History:
*   Create: gxf. 2009-03-28
*
********************************************************************/

#include "arch/x86/X86AsmGenerator.h"
#include "arch/x86/X86Code.h"

using namespace crossbit;

X86AsmGenerator::funcTab[] = {NULL};

// X86 Wrapper class for register allocation
XTRegNum X86AsmGenerator::X86RegAlloc(XTRegNum vreg, RegAccessMode mode){
    XTRegNum alloc = reg_allocator->regAlloc(vreg, mode);

    if (alloc == X86::ESP){
        dirty_esp = true;
    } 
    return alloc;
}

XTRegNum X86AsmGenerator::X86RegAllocExpect(XTRegNum vreg, XTUint8 reg_mask, RegAccessMode mode){
    return reg_allocator -> regAllocExpect( vreg , reg_mask , mode );
}

XTRegNum X86AsmGenerator::X86RegAllocForce(XTRegNum vreg, XTRegNum expect, RegAccessMode mode){
    XTRegNum alloc = reg_allocator->regAllocForce(vreg, expect, mode);

    if (alloc == X86::ESP){
        dirty_esp = true;
    }
    return alloc;
}

XTRegNum X86AsmGenerator::X86RegAllocForceExcept(XTRegNum vreg, XTRegNum except, RegAccessMode mode){
    XTRegNum alloc = reg_allocator->regAllocForceExcept(vreg, except, mode);

    if (alloc == X86::ESP){
        dirty_esp = true;
    }
    return alloc;
}


void X86AsmGenerator::X86RegAllocReserve(XTRegNum treg){
    reg_allocator->regAllocReserve(treg);
}

void X86AsmGenerator::X86RegAllocRelease(XTRegNum treg){
    reg_allocator->regAllocRelease(treg);
}

//TODO
/*
void X86Translator::setBlockStub()
{
		//Added by suntingtao
		// test whether the current tblock is a super block , if not , increase it's 
		//'run_count' counter, if the value arrive the threshold of a super block it ret directly.
		if( !tblock -> isSupperBlock() ){
				X86::movwMemToReg( tblock -> getRunCountAddr() , X86::EAX );
				X86::inc( X86::EAX );
				X86::cmpImmWithReg( GLOBAL::THRESHOLD , X86::EAX ); 	 

				//if THRESHOLD > EAX , jump to the next third instruction
				X86::jccRelativeDirectDisp32( 15 , X86::LE );  

				X86::movwImmToMem((XTMemAddr)tblock, (XTMemAddr)(&GLOBAL::BUILD_SUPER_BLOCK));
   				X86::jmpRelativeDirectDisp32( GLOBAL::switch_addr - ( X86::getP() + 5 ) ); 

				X86::movwRegToMem( X86::EAX , tblock -> getRunCountAddr());

		}

}
// changed by suntingtao on 2009-02-19
// to revome the save esp, 
void X86Translator::set_switch_point( XTMemAddr point ) {
	X86::setP ( point ) ;
	X86::movwMemToReg((XTMemAddr)(&(GLOBAL::saved_esp)), X86::ESP); 
	X86::ret();
}		
// the hash dispatch codes
void X86Translator::set_hashtable_dispatch( XTMemAddr addr ) {
	X86::setP (addr);
	X86::movwImmToReg(GLOBAL::TABLE_MASK, X86::EAX); 
    X86::andRegToReg( X86::EDX , X86::EAX );	
	// indirect_hashtable + eax * 8 -> ebx
	X86::leaRegToReg( X86::EAX , (XTMemAddr)(GLOBAL::indirect_hashtable) , DWORD , X86::ECX); 
	X86::movwMemToReg( X86::ECX , X86::EAX );
	X86::cmpRegWithReg( X86::EDX , X86::EAX );
	X86::jneRelativeDirectDisp32( GLOBAL::switch_addr - (X86::getP() + 6 ) );
	X86::movwMemToReg( X86::ECX , 4 , X86::EAX ); 
	X86::jmpAbsoluteIndirectReg( X86::EAX );
}		
*/
X86AsmGenerator::X86AsmGenerator(){
#define FILL_FUNC_TABLE(INS) \
    funcTab[OP_##INS##] = encode##INS 
    FILL_FUNC_TABLE(GET);
    FILL_FUNC_TABLE(PUT);
    FILL_FUNC_TABLE(LD);
    FILL_FUNC_TABLE(ST);
    FILL_FUNC_TABLE(LI);
    FILL_FUNC_TABLE(LIRETURN);
    FILL_FUNC_TABLE(RETURN);
    FILL_FUNC_TABLE(MOV);
    FILL_FUNC_TABLE(SEXT);
    FILL_FUNC_TABLE(ZEXT);
    FILL_FUNC_TABLE(ADD);
    FILL_FUNC_TABLE(ADDI);
    FILL_FUNC_TABLE(SUB);
    FILL_FUNC_TABLE(AND);
    FILL_FUNC_TABLE(XOR);
    FILL_FUNC_TABLE(OR);
    FILL_FUNC_TABLE(NOT);
    FILL_FUNC_TABLE(SLL);
    FILL_FUNC_TABLE(SRL);
    FILL_FUNC_TABLE(SRA);
            /*
    FILL_FUNC_TABLE(SLLV);
    FILL_FUNC_TABLE(SRLV);
    FILL_FUNC_TABLE(SRAV);
            */
    FILL_FUNC_TABLE(MUL);
    FILL_FUNC_TABLE(MULU);
    FILL_FUNC_TABLE(DIV);
    FILL_FUNC_TABLE(DIVU);
    FILL_FUNC_TABLE(CMP);
    FILL_FUNC_TABLE(BRANCH);
    FILL_FUNC_TABLE(JMP);
    FILL_FUNC_TABLE(HALT);
//    FILL_FUNC_TABLE(SBREAK);
    FILL_FUNC_TABLE(SYSCALL);
    FILL_FUNC_TABLE(CALL);

    FILL_FUNC_TABLE(OP_FPUT_S);
    FILL_FUNC_TABLE(OP_FGET_S);
    FILL_FUNC_TABLE(OP_FPUT_D);
    FILL_FUNC_TABLE(OP_FGET_D);
    FILL_FUNC_TABLE(OP_FLD);
    FILL_FUNC_TABLE(OP_FST);
    FILL_FUNC_TABLE(OP_FMOV); 
    FILL_FUNC_TABLE(OP_FADD_S);
    FILL_FUNC_TABLE(OP_FADD_D);
    FILL_FUNC_TABLE(OP_FSUB_S);
    FILL_FUNC_TABLE(OP_FSUB_D);
    FILL_FUNC_TABLE(OP_FMUL_S);
    FILL_FUNC_TABLE(OP_FMUL_D);
    FILL_FUNC_TABLE(OP_FDIV_S);
    FILL_FUNC_TABLE(OP_FDIV_D);
    FILL_FUNC_TABLE(OP_FNEG_S);
    FILL_FUNC_TABLE(OP_FNEG_D);
    FILL_FUNC_TABLE(OP_FABS_S);
    FILL_FUNC_TABLE(OP_FABS_D);
    FILL_FUNC_TABLE(OP_FSQRT_S);
    FILL_FUNC_TABLE(OP_FSQRT_D);
    FILL_FUNC_TABLE(OP_FCMP_S);
    FILL_FUNC_TABLE(OP_FCMP_D);
    FILL_FUNC_TABLE(OP_FCONV);
    FILL_FUNC_TABLE(OP_FINCSTP);
#undef FILL_FUNC_TABLE
}

AsmBlock *X86AsmGenerator::GenPseudoAsm(VBlock *vb){ 
    // Init register allocator
    reg_allocator->init(vb);

    // TODO: header stub for super block
//    setBlockStub();

    // TODO: unknown meaning
    begin_exit = GLOBAL::total_exit;

    VBlock::vb_iterator it = vblock->begin();
    for (;it != vblock->end(); ++it, reg_allocator->incrementVinstSeq()) {
        Opcode op = (*it) -> getOpcode();

        assert(funcTab[op] != NULL);
        (*funcTab[op])(); 

    }
    
    // Add Into-ExecEngine jump and END_OF_BLOCK exit 
    // if the last instruction is not unconditional jump
    if ( inst->getOpcode() == OP_BRANCH) 
        // TODO
        endOfBlock();
		
    encodeExits();
}
#if 0
void X86Translator::endOfBlock()
{
		XTInt32 i = addTBlockExit(ExitReason::ENDOFBLOCK, 
						vblock->nextSPC(),	
						X86::getP() - 4 - inst_buf,
						NULL);
		Exit* e = GLOBAL::getExit( i );
		e -> link_type = LinkType::DIRECT;
        e -> linkCode = (XTInt8 *)(X86::getP() + 1);
		// dispatch later
   		X86::jmpRelativeDirectDisp32( 0 ); 

}

XTInt32 X86Translator::addTBlockExit(XTInt32 why, XTMemAddr next, XTInt32 exitOffset, VInst *inst)
{
		Exit* exit = GLOBAL::requestExit();
		exit -> why = why;
		exit -> next = next;
		exit -> exitOffset = exitOffset;
		exit -> inst = inst;
		exit -> linked = 0;
		exit -> linkCode = NULL;
		exit -> dest = 0;
        exit -> tblock_pointer = tblock;
        return exit -> exit_number;
}

void X86Translator::encodeExits()
{

		XTMemAddr temp = X86::getP();
	    X86::setP( GLOBAL::exit_stub_base );	
		for (XTInt32 i = begin_exit; i < GLOBAL::total_exit; ++i) {
			Exit* e = GLOBAL::getExit(i);
			e -> dest = X86::getP();
			if( e -> link_type == LinkType::NOLINK ) {
				continue;
			} 
			//printf(" e -> linkCode = %x\n" , e -> linkCode );
			*(XTInt32*)( e -> linkCode ) = e -> dest - (( XTMemAddr )( e -> linkCode ) + 4 ); 
			X86::movwImmToReg(i , X86::EBX);
   			X86::jmpRelativeDirectDisp32( GLOBAL::switch_addr - ( X86::getP() + 5 ) ); 
		}
		GLOBAL::exit_stub_base = X86::getP();
		assert( GLOBAL::exit_stub_base < GLOBAL::exit_stub_end && "XTERROR : the exit_stub_base < exit_stub_end\n" );
		X86::setP(temp);
}
#endif


void X86Translator::encodeGET()
{
    // DEBUG(std::cerr << "  Encoding GET" << std::endl);
    // GET s, v
    XTMemAddr addr = src_state->get(inst->getOperand(1).reg);
    XTRegNum reg = inst->getOperand(2).reg;
    //assert(reg != 0);
    XTRegNum Tdst = X86RegAlloc( reg , DEF ) ;
	if( inst -> getOperand(1).reg == 0 ) {
		X86::xorRegToReg(Tdst, Tdst);
	} else {	
    	X86::movwMemToReg(addr, X86RegAlloc(reg, DEF));
	}
}
